home *** CD-ROM | disk | FTP | other *** search
/ Amiga Collections: Taifun / Taifun 054 (1988-05-15)(Ossowski, Stefan)(DE)(PD).zip / Taifun 054 (1988-05-15)(Ossowski, Stefan)(DE)(PD).adf / MRBackup / MRBackup2.0 / Backup.c < prev    next >
C/C++ Source or Header  |  1988-04-09  |  23KB  |  933 lines

  1. /* MRBackup: Backup Routines
  2.  * Filename:    Backup.c
  3.  * Author:        Mark R. Rinfret
  4.  * Date:        09/04/87
  5.  *
  6.  * History:        (most recent change first)
  7.  *
  8.  * 12/29/87 -MRR- Enable "big file" backup.
  9.  *
  10.  * 11/19/87 -MRR- Add the listing file name to the exclusion list.
  11.  *                Also, add some more error handling.
  12.  *
  13.  * 09/04/87 -MRR- This package was (finally) extracted from Main.c.
  14.  */
  15.  
  16. #define MAIN
  17.  
  18. #include "MRBackup.h"
  19. #include ":src/lib/DiskMisc.h"
  20.  
  21. T_FILE *AddFile();
  22. void DisposeList();
  23. USHORT FileSize();
  24. T_FILE *MakeFileNode();
  25.  
  26. static unsigned filesInDir;        /* number of files in current directory */
  27. T_FILE *savedDir;                /* Current directory at start of volume */
  28. T_FILE_LIST savedList;            /* File list at start of volume */
  29.  
  30. ^L
  31. /* Add a filename to the list of excluded files.
  32.  * Called with:
  33.  *        name:        filename to be excluded
  34.  */
  35. AddExclude(name)
  36.     char *name;
  37. {
  38.     T_PATTERN *p;
  39.  
  40.     p = (T_PATTERN *) calloc(sizeof(T_PATTERN), 1);
  41.     p->pattern = calloc(strlen(name)+1, 1);
  42.     strcpy(p->pattern, name);
  43.     if ( ! excludeList ) 
  44.         excludeList = p;
  45.     else
  46.         lastExclude->next_pattern = p;
  47.     lastExclude = p;
  48. }
  49. ^L
  50. /* Add a file/directory name to the list.
  51.  * Called with:
  52.  *        name:        file/dir name string
  53.  *        blocks:        size of file/directory in blocks (dir => 1)    
  54.  *        isDir:        true => this is a directory name
  55.  *        list:        address of list header
  56.  * Returns:
  57.  *        pointer to new node or NULL (out of memory)
  58.  * Notes:
  59.  *        The filename string MUST NOT contain the volume component,
  60.  *        since it is used to build both source and destination
  61.  *        pathnames.  Also note that this routine inserts the name
  62.  *        into the list in sorted order (case sensitive).  Though this
  63.  *        changes the "natural order" of the files, it makes them 
  64.  *        easier to locate on the backup disks.
  65.  */
  66.  
  67. T_FILE *
  68. AddFile(name, blocks, isDir, list)
  69.     char *name; USHORT blocks; BOOL isDir; T_FILE_LIST *list;
  70. {
  71.     T_FILE *fnode, *tnode;
  72.  
  73.     if (!(fnode = MakeFileNode(name)))
  74.         return NULL;
  75.  
  76.     fnode->blocks = blocks;
  77.     fnode->is_dir = isDir;
  78.  
  79.     if (!list->first_file) {            /* file list is empty? */
  80.         list->first_file = fnode;        /* this is the new head */
  81.     }
  82.     else {
  83.         /* Find the insertion point for this file. */
  84.  
  85.         for (tnode = list->first_file; tnode; tnode = tnode->next) {
  86.             if (strcmp(fnode->filename, tnode->filename) <= 0) {
  87.                 fnode->next = tnode;    /* insert it here */
  88.                 if (tnode->previous)
  89.                     tnode->previous->next = fnode;
  90.                 fnode->previous = tnode->previous;
  91.                 tnode->previous = fnode;
  92.                 if (list->first_file == tnode)
  93.                     list->first_file = fnode;
  94.                 return fnode;
  95.             }
  96.         }
  97.  
  98.         /* Append file node to the end of the list. */
  99.  
  100.         fnode->previous = list->last_file;
  101.         list->last_file->next = fnode;
  102.     }
  103.     list->last_file = fnode;
  104.     return fnode;
  105. }
  106. ^L
  107. /* Main backup routine. */
  108.  
  109. Backup()
  110. {
  111.     int compare_flag, status = 0;
  112.  
  113.     errorCount = 0;
  114.  
  115.     Speak("O K,  let's get to work.");
  116.     DateStamp(now);
  117.     mainList.first_file = mainList.last_file = currentDir = NULL;
  118.  
  119.     if (doListing) {
  120.         if ( OpenList(listPath) ) return;
  121.     }
  122.  
  123.     /* Get the file comparison date.  Only files created on or after
  124.      * this date are backed up.
  125.      */
  126.     do {
  127.         *since = *now;
  128.         since->ds_Days -= 1;        /* default interval is 1 day */
  129.         since->ds_Minute = 0;
  130.         since->ds_Tick = 0;
  131.         Speak("Enter the date since your last backup.");
  132.         DateRequest(mainWindow, "Backup files since:",since, since);
  133.         if ( (compare_flag = CompareDS(since, now) ) >= 0) 
  134.             DisplayBeep(NULL);
  135.     } while (compare_flag >= 0);
  136.  
  137.     BreakPath(homePath, srcVol, srcPath);
  138.     strcat(srcVol, ":");
  139.     BreakPath(backPath, destDrive, destPath);
  140.  
  141. #ifdef DEBUG
  142.     sprintf(debugMsg, "destDrive = %s, destPath = %s\n",destDrive,destPath);
  143.     DebugWrite(debugMsg);
  144.     sprintf(debugMsg, "srcVol = %s, srcPath = %s\n", srcVol,srcPath);
  145.     DebugWrite(debugMsg);
  146. #endif
  147.  
  148.     if (*destPath) {
  149.         TypeAndSpeak("The backup path must be a device name.");
  150.         status = ERR_ABORT;
  151.         goto done;
  152.     }
  153.     strcat(destDrive,":");
  154.  
  155. /* !!! Failure of GetExcludes should not prevent backup. */
  156.  
  157.     if (*excludePath)
  158.         if (GetExcludes()) goto done;
  159.  
  160.     AddExclude(listPath);        /* don't try to backup list (if file) */
  161.  
  162.     level = 0;
  163.     diskNumber = 0;
  164.     sizeLeft = 0;
  165.     totalSize = 0;
  166.     savedList.first_file = NULL;
  167.     SetGauge(sizeLeft, totalSize);
  168.  
  169.     /* Force a new disk right away. */
  170.  
  171.     if (status = CheckSize(false)) goto done;
  172.  
  173.     if (*srcPath) {                /* starting path is a directory */
  174.         if (!AddFile(srcPath, 1, true, &mainList))
  175.             status = ERROR_NO_FREE_STORE;
  176.     }
  177.     else                        /* starting path is a device */
  178.         status = CollectFiles(srcPath,&mainList);
  179.  
  180.     if (!status) {
  181. restart:
  182.         while (mainList.first_file) {    /* while something in the list */
  183.             if (status = BackupFiles(&mainList))     break;
  184.             if (status = BackupDirs(&mainList))     break;
  185.         }
  186.     }
  187. done:
  188.     if (status == 0) {
  189.         TypeAndSpeak("I am done, and everything seems to be O K.\n");
  190.         TypeAndSpeak("It was a pleasure working with you.\n");
  191.     }
  192.     else {
  193.         if (status == ERR_RESTART_VOLUME) {
  194.             if ( ! ( status = RestoreContext() ) ) {
  195.                 sizeLeft = 0;
  196.                 SetGauge(sizeLeft, totalSize);
  197.                 NewLine(2);
  198.                 ListLine("*** Restarting volume ***");
  199.                 goto restart;
  200.             }
  201.         }
  202.         else if (status != ERR_ABORT) {
  203.             TypeAndSpeak("Things are not well, my friend.\n");
  204.             sprintf(conmsg,"Your backup terminated with error %d.\n",status);
  205.             TypeAndSpeak(conmsg);
  206.         }
  207.     }
  208.     DisposeList(&mainList);
  209.     DisposeList(&savedList);
  210.     SetCurVolumeGadget("");
  211. }
  212.  
  213. ^L
  214. /* Process the next directory in the file list.
  215.  * This routine is recursive.
  216.  * Called with:
  217.  *        list:        file list to be processed
  218.  * Returns:
  219.  *        status code    (0 => success)
  220.  */
  221.  
  222. int
  223. BackupDirs(list)
  224.     T_FILE_LIST *list;
  225. {
  226.     T_FILE *dirNode;
  227.     T_FILE *saved_currentDir;
  228.     int     status = 0;
  229.     T_FILE_LIST sublist;                /* subdirectory list header */
  230.  
  231.     sublist.first_file = sublist.last_file = NULL;
  232.     saved_currentDir = currentDir;    /* remember current context */
  233.  
  234. /* There are a couple of things to note here.  The first is that once
  235.  * we have reached here, there should be NO simple file nodes in "list".
  236.  * That currently is not handled as an error, but probably should be.
  237.  * Second, since this scan modifies the list, a removal of a directory
  238.  * node starts the scan at the beginning of the list since we shouldn't
  239.  * reference the links in the node we're removing.  Since we should be
  240.  * removing the first node in the list anyway, who cares?
  241.  */
  242.     for (dirNode = list->first_file; dirNode; ) {
  243.         if (dirNode->is_dir) {            /* found one */
  244.             currentDir = dirNode;        /* set current directory */
  245.             RemFile(dirNode, list);
  246.  
  247. /*            if (status = NewDir(currentDir->filename)) break; */
  248.  
  249.             if (status = CollectFiles(currentDir->filename,&sublist)) 
  250.                 break;
  251.             if (status = BackupFiles(&sublist)) break;
  252.             if (status = BackupDirs(&sublist))  break;
  253.             dirNode = list->first_file;
  254.         }
  255.         else                            /* should never get here !!! */
  256.             dirNode = dirNode->next;
  257.     }
  258.     currentDir = saved_currentDir;
  259.     return status;
  260. }
  261. ^L
  262. /* Backup all simple files in the current list.
  263.  * Called with:
  264.  *        list:        file list header
  265.  * Returns:
  266.  *        status code (0 => success)
  267.  */
  268. int
  269. BackupFiles(list)
  270.     T_FILE_LIST *list;
  271. {
  272.     T_FILE *primary, *secondary;
  273.     int status = 0;
  274.  
  275. /* The following loop continually scans the file list (from the front),
  276.  * looking for more file entries to process.  If the primary choice
  277.  * fails, an attempt is made to find a file which will fit in the
  278.  * space remaining.  If that attempt fails, then a new disk is formatted.
  279.  */
  280.  
  281.     filesInDir = 0;                    /* Nothing in directory yet */
  282.  
  283.     while (primary = FindFile(list,false)) { /* Find next file to process. */
  284.         if (primary->blocks >= totalSize) { /* It's a biggy! */
  285.             if ( ! (doBigFiles || doFormat) )  {
  286.  
  287.                 /* "Big files" may only be backed up if "Allow Big Files" is
  288.                  * enabled along with "Format Destination".
  289.                  */
  290.                 sprintf(conmsg,"%s is too big to back up!\n",
  291.                         primary->filename);
  292.                 TypeAndSpeak(conmsg);
  293.                 TypeAndSpeak(
  294. "In order to back up big files, you will have to select the Allow Big Files"
  295.                     );
  296.                 TypeAndSpeak(
  297.                     "and Format Destination options in the Flags menu."
  298.                     );
  299.             } else {                        /* Start crankin'! */
  300.                 status = DoFile(primary);
  301.             }
  302.         }
  303.         else {
  304.             if (primary->blocks >= sizeLeft) {     /* file doesn't fit */
  305.                 if (!(secondary = FindFile(list,true))) {
  306.                     /* At this point, we know that there's at least one
  307.                      * file to back up, but none that fit.  Start a new
  308.                      * disk.
  309.                      */
  310.                     if (status = NewDisk(true)) return status;
  311.                     continue;                /* try that file again */
  312.                 }
  313.             primary = secondary;        /* use second choice */
  314.             }
  315.             if (status = DoFile(primary)) return status;
  316.         }
  317.         RemFile(primary,list);            /* delete the node */
  318.     }
  319.     if (currentDir) {                    /* forget current directory */
  320.         FreeFile(currentDir);
  321.         currentDir = NULL;
  322.     }
  323.     return status;
  324. }
  325. ^L
  326. /* Check the file name about to be added against the exclude patterns.
  327.  * Called with:
  328.  *        name:    pathname to be checked
  329.  * Returns:
  330.  *        0 => no match
  331.  *        1 => name was matched, ignore it.
  332.  */
  333.  
  334. int
  335. CheckExclude(name)
  336.     char *name;
  337. {
  338.     int match = 0;
  339.     T_PATTERN *p;
  340.  
  341.     for (p = excludeList; p; p = p->next_pattern) {
  342.         if (match = wildcmp(p->pattern, name)) { 
  343.             sprintf(conmsg,"Excluding %s\n", name);
  344.             WriteConsole(conmsg);
  345.             break;
  346.         }
  347.     }
  348.     return match;
  349. }
  350.  
  351. ^L
  352. /* Check the current number of disk blocks (sizeLeft) available.  If
  353.  * less than 1, it's time to format a new disk.
  354.  * Called with:
  355.  *        create_dir:        true => OK to create continuation directory
  356.  * Returns:
  357.  *        0 => success
  358.  *        1 => failure
  359.  */
  360.  
  361. int
  362. CheckSize(create_dir)
  363.     int create_dir;
  364. {
  365.     if (sizeLeft > 0) return 0;
  366.     return NewDisk(create_dir);
  367. }
  368. ^L
  369. /* Collect file names from a starting path.
  370.  * Called with:
  371.  *        name:        starting pathname
  372.  *                        Note that name may be a null string when copying
  373.  *                        the entire home device.
  374.  *        list:        pointer to file list header
  375.  * Returns:
  376.  *        status code (0 => success)
  377.  * Notes:
  378.  *        CollectFiles attempts to collect all file and directory names
  379.  *        for a given level, starting with "name".  If a simple filename
  380.  *        is given as a starting path, only that name will be collected.
  381.  *        If a directory name is given, then all pathnames contained
  382.  *        within that directory (only) will be collected.  For each
  383.  *        directory name collected, CollectFiles will be called again to
  384.  *        collect files for that particular directory.  This iterative
  385.  *        approach (vs. recursive) saves memory and allows us to maintain
  386.  *        order at each directory level.
  387.  */
  388.  
  389. int
  390. CollectFiles(name, list)
  391.     char *name; T_FILE_LIST *list;
  392. {
  393.     int     status = 0;
  394.     struct FileInfoBlock   *FIB = NULL;
  395.     T_FILE *fnode;            /* file descriptor node */
  396.     struct Lock *lock = NULL;    
  397.     char path[PATH_MAX+1];
  398.     USHORT top_level;
  399.  
  400. #ifdef DEBUG
  401.     sprintf(debugMsg,"Collecting files from %s\n",name);
  402.     DebugWrite(debugMsg);
  403. #endif
  404.  
  405.     if (CheckStop()) return ERR_ABORT;
  406.  
  407.     top_level = (*name == '\0');    /* empty name implies top level */
  408.  
  409.     if (!(FIB =
  410.         AllocMem((long)sizeof(struct FileInfoBlock),
  411.                  MEMF_CHIP|MEMF_CLEAR))) {
  412.         TypeAndSpeak("CollectFiles: Can not allocate memory for FIB\n");
  413.         return ERROR_NO_FREE_STORE;
  414.     }
  415.  
  416.     strcpy(path,srcVol);            /* rebuild current home path */
  417.     strcat(path, name);
  418.  
  419.     if (!(lock = (struct Lock *) Lock( path, SHARED_LOCK ))) {
  420.         status = IoErr();
  421.         sprintf(conmsg,"CollectFiles can not lock %s; error %d\n",
  422.             path, status);
  423.         TypeAndSpeak(conmsg);
  424.         goto out2;
  425.     }
  426.  
  427.     if ((Examine(lock,FIB))==0){
  428.         status = IoErr();
  429.         sprintf(conmsg,"CollectFiles can not examine %s; error: %d\n",
  430.                 name, status);
  431.         TypeAndSpeak(conmsg);
  432.         goto out2;
  433.     }
  434.  
  435.     if (FIB->fib_DirEntryType > 0){    /* "name" is a directory */
  436.  
  437.         while(!status && ExNext(lock, FIB)) {
  438.             if (CheckStop()) {
  439.                 status = ERR_ABORT;
  440.                 goto out2;
  441.             }
  442.             /* First, check the file against the exclusion list. */
  443.             if (CheckExclude(FIB->fib_FileName))
  444.                 continue;
  445.  
  446.             if (FIB->fib_DirEntryType < 0) {
  447.  
  448.                 /* Check the file date. */
  449.  
  450.                 if (CompareDS(&FIB->fib_Date, since) < 0) {
  451. #ifdef DEBUG
  452.                     sprintf(debugMsg,"Skipping %s\n",&FIB->fib_FileName[0]);
  453.                     DebugWrite(debugMsg);
  454. #endif
  455.                     continue;
  456.                 }
  457.             }
  458.             if (top_level)
  459.                 *path = '\0';
  460.             else {
  461.                 strcpy(path,name);
  462.                 strcat(path,"/");
  463.             }
  464.             strcat(path,FIB->fib_FileName);
  465.             if (!AddFile(path, FileSize(FIB), 
  466.                             (FIB->fib_DirEntryType >= 0), list))
  467.                 status = ERROR_NO_FREE_STORE;
  468.         }
  469.         /* !!! Need check here for ERROR_NO_MORE_ENTRIES */
  470.     }
  471.     else {
  472.         if ( ! CheckExclude(FIB->fib_FileName) &&
  473.              (CompareDS(&FIB->fib_Date, since ) >= 0) ) {
  474.             if( ! AddFile(name, FileSize(FIB),
  475.                           (FIB->fib_DirEntryType >= 0), list))
  476.                 status = ERROR_NO_FREE_STORE;
  477.             }
  478.         }
  479. out2: 
  480.     UnLock(lock);
  481. out1: 
  482.     FreeMem(FIB, (long) sizeof(struct FileInfoBlock) );
  483.  
  484.     /* Don't give up if somebody else is using the file - just
  485.      * ignore the error.  The user can cancel us if there's a
  486.      * problem.
  487.      */
  488.  
  489.     if (status == ERROR_OBJECT_IN_USE) status = 0;
  490.     return status;
  491. }
  492. ^L
  493. /* Dispose of a file info list.
  494.  * Called with:
  495.  *        list:        pointer to list structure
  496.  */
  497. void
  498. DisposeList(list)
  499.     T_FILE_LIST *list;
  500. {
  501.     while (list->first_file) RemFile(list->first_file, list);
  502. }
  503.  
  504. ^L
  505. /* Process one file.
  506.  * Called with:
  507.  *        fnode:            node describing file
  508.  * Returns:
  509.  *        0    => success
  510.  *        1    => failure
  511.  */
  512. int
  513. DoFile(fnode)
  514.     T_FILE *fnode;
  515. {
  516. #define MAX_RETRY    3
  517.  
  518.     int     status = ERR_NONE;
  519.     char    destName[PATH_MAX+1];
  520.     BOOL    fileIsBig;
  521.     int        oldSize;
  522.     char    srcName[PATH_MAX+1];
  523.  
  524.     if (++filesInDir == 1 && currentDir) {    
  525.  
  526.         /* Create directory on first file. */
  527.  
  528.         if (status = NewDir(currentDir->filename))
  529.             return status;
  530.         /* Directory info is listed in NewDir, need not be done here. */
  531.     }
  532.  
  533.     /* If we got here with a big file, it's because doBigFiles is true.
  534.      * If we can't get at least 20 blocks of the file onto this disk,
  535.      * then allow CheckSize to ask for a new one.
  536.      */
  537.  
  538.     fileIsBig = (fnode->blocks >= totalSize);
  539.     oldSize = sizeLeft;
  540.     sizeLeft -= fnode->blocks;        /* deduct blocks for file */
  541.  
  542.     if (!fileIsBig || (oldSize < 20))
  543.         if ( CheckSize(true) )            /* check disk space */
  544.             return ERR_ABORT;
  545.  
  546.     if (sizeLeft > oldSize)            /* we just formatted */
  547.         oldSize = sizeLeft;
  548.  
  549. /*#define NOCOPY*/                        /* for fast debugging */
  550.     do {
  551.         if (status = CheckStop())    /* does user want out? */
  552.             return status;
  553.  
  554.         sprintf(srcName,"%s%s",srcVol,fnode->filename);
  555.         sprintf(conmsg,"Blocks left: %5d   ",oldSize);
  556.         WriteConsole(conmsg);
  557.  
  558.         if ( !doCompress ||             /* not compressing files? */
  559.              IsCompressed(srcName) ||     /* file already compressed? */
  560.              fileIsBig ||                 /* file too big to compress? */
  561.              fnode->blocks < 4             /* file too small to compress? */
  562.             ) {
  563.             sprintf(destName,"%s%s",destVol,fnode->filename);
  564. #ifndef NOCOPY
  565.             if (fileIsBig) {
  566.                 sprintf(conmsg,
  567.                 "Doing multi-volume backup of %s\n",fnode->filename);
  568.                 WriteConsole(conmsg);
  569.                 status = BackupBigFile(fnode->filename);
  570.             }
  571.             else {
  572.                 sprintf(conmsg,"Copying %s\n",fnode->filename);
  573.                 WriteConsole(conmsg);
  574.                 status = CopyFile(srcName,destName);
  575.             }
  576. #endif
  577.         }
  578.         else {
  579.             sprintf(destName,"%s%s.Z",destVol,fnode->filename);
  580.             sprintf(conmsg,"Compressing %s\n",fnode->filename);
  581.             WriteConsole(conmsg);
  582. #ifndef NOCOPY
  583.             if (!(status = compress(srcName,destName)))
  584.                 status = CopyFileDate(srcName,destName);
  585. #endif
  586.         }
  587.         if (status) {
  588.             sprintf(conmsg,
  589.                 "Oh darn it!  I got error %d on file %s.\n", status,
  590.                 fnode->filename);
  591.             TypeAndSpeak(conmsg);
  592.             NewLine(1);
  593.             ListLine(conmsg);
  594.             NewLine(1);
  595.             unlink(destName);
  596.             ++errorCount;
  597.             SetErrorGadget();        /* Update error counter */
  598.             status = GetErrOpt(
  599.                         ERR_ABORT | ERR_IGNORE | 
  600.                         ERR_RETRY_FILE | ERR_RESTART_VOLUME);
  601.         }
  602.         else
  603.             ListFileInfo(destName);
  604.  
  605.     } while (status == ERR_RETRY_FILE);
  606.  
  607.     if (status == ERR_IGNORE) status = ERR_NONE;
  608.  
  609. #ifndef NOCOPY
  610.     if ( !status ){
  611.         if ((sizeLeft = DiskBlocksLeft(destVol)) < 0)/* update blocks left */
  612.             status = -sizeLeft;
  613.         else
  614.             SetGauge(sizeLeft, totalSize);
  615.     }
  616. #endif
  617.     return status;
  618. }
  619.  
  620. /* Compute the size of a file, in blocks, from its file information block.
  621.  * If the FIB pointer is NULL, assume that the file is a directory.
  622.  * Called with:
  623.  *        FIB:        file information block (can be NULL)
  624.  * Returns:
  625.  *        Number of disk blocks required by file.
  626.  */
  627.  
  628. USHORT
  629. FileSize(FIB)
  630.     struct FileInfoBlock *FIB;
  631. {
  632.     USHORT blocks;
  633.     if (!FIB) {
  634.         blocks = 1;
  635.     }
  636.     else if ( FIB->fib_DirEntryType >= 0 )
  637.         blocks = 1;                /* assume 1 block for directory */
  638.     else {
  639.         blocks = ((FIB->fib_Size/488)+2); /* 488 = bytesinBlock - ovhd */
  640.         blocks += (blocks/70);
  641.     }
  642.     return blocks;
  643. }
  644. ^L
  645. /* Attempt to find a file node in the file list.  If the check_size
  646.  * parameter is true, only look at files which will fit in the space
  647.  * remaining on the disk.
  648.  * Called with:
  649.  *        list:        pointer to file list to be searched
  650.  *        check_size: false => find first file in the list
  651.  *                    true  => test space available
  652.  * Returns:
  653.  *        file node or NULL (not found)
  654.  */
  655.  
  656. T_FILE *FindFile(list,check_size)
  657.     T_FILE_LIST *list; int check_size;
  658. {
  659.     T_FILE *fnode;
  660.  
  661.     for (fnode = list->first_file; fnode; fnode = fnode->next) {
  662.         if (!fnode->is_dir) {        /* don't consider directory nodes */
  663.             if (!check_size) break;    /* take this one */
  664.             if (fnode->blocks < sizeLeft) break;
  665.         }
  666.     }
  667.     return fnode;
  668. }
  669.  
  670. /* Free memory allocated to a file node.
  671.  * Called with:
  672.  *        node:    file node
  673.  */
  674. FreeFile(node)
  675.     T_FILE *node;
  676. {
  677.  
  678.     free(node->filename);
  679.     free(node);
  680. }
  681. ^L
  682. /* An exclude file pathname has been specified.  Get the patterns it
  683.  * contains.
  684.  * Returns:
  685.  *        status:    0 => success, failure otherwise
  686.  */
  687. int
  688. GetExcludes()
  689. {
  690.     USHORT i, length, nonwild;
  691.     T_PATTERN *p;
  692.     int status = 0;
  693.     char str[PATH_MAX+1];
  694.     FILE *xcld;
  695.  
  696.     if (! excludeHasChanged)
  697.         return 0;
  698.  
  699.     if (!(xcld = fopen(excludePath, "r"))) {
  700.         sprintf(conmsg, 
  701.             "I couldn't open the exclude pattern file: error %d.", errno);
  702.         TypeAndSpeak(conmsg);
  703.         return errno;
  704.     }
  705.  
  706.     /* Release any previous exclude list. */
  707.  
  708.     for (p = excludeList; p; p = p->next_pattern) {
  709.         free(p->pattern);
  710.         free(p);
  711.     }
  712.     excludeList = lastExclude = NULL;
  713.  
  714.     while (fgets(str, PATH_MAX, xcld)) {
  715.         if (length = strlen(str)) {
  716.             --length;
  717.             str[length] = '\0';
  718.         }
  719.         if (length && *str != '#') {    /* ignore blank lines and comments */
  720.             nonwild = 0;
  721.             for (i = 0; i < length; ++i) {
  722.                 if (str[i] != '*' && str[i] != '?' && str[i] != '/')
  723.                     ++nonwild;
  724.             }
  725.             if (! nonwild ) {
  726.                 sprintf(conmsg,
  727.                 "Very funny!  %s will exclude everything!  Ha ha ha!\n",
  728.                     str);
  729.                 TypeAndSpeak(conmsg);
  730.                 status = ERR_ABORT;
  731.                 goto done;
  732.             }
  733.             AddExclude(str);
  734.         }
  735.     }
  736. done:
  737.     fclose(xcld);
  738.     if (! status )
  739.         excludeHasChanged = false;
  740.     return status;
  741. }
  742.  
  743. /* Create a new file information node.
  744.  * Called with:
  745.  *        name:        name of file/directory
  746.  * Returns:
  747.  *        pointer to file node or NULL (out of memory)
  748.  */
  749.  
  750. T_FILE *
  751. MakeFileNode(name)
  752.     char *name;
  753. {
  754.     T_FILE *fnode;
  755.  
  756.     if (!(fnode = (T_FILE *) calloc(1,sizeof(T_FILE)))) {
  757. nomem:
  758.         TypeAndSpeak("I have run out of memory!\n");
  759.     }
  760.     else {
  761.         if ( ! (fnode->filename = calloc(1, strlen(name)+1) ) ) 
  762.             goto nomem;
  763.         strcpy(fnode->filename, name);        /* copy the filename */
  764.     }
  765.     return fnode;
  766. }
  767.  
  768. ^L
  769. /* Format a new diskette.
  770.  * Called with:
  771.  *        create_dir:    true => create continuation directory, if necessary
  772.  * Returns:
  773.  *        false => success
  774.  *        true  => failure
  775.  */
  776.  
  777. int
  778. NewDisk(create_dir)
  779.     int create_dir;
  780. {
  781.     char datestr[20];
  782.     char *diskPrompt;
  783.     int status = 0;
  784.  
  785.     if (diskNumber)                        /* not first disk? */
  786.         Delay(TICKS_PER_SECOND * 3L);    /* let disk buffers flush */
  787.  
  788.     ++diskNumber;                        /* Increment the volume ID */
  789.     if (doFormat) {
  790.         Speak("Attention!  I am ready to format a new disk.");
  791.         diskPrompt = "Insert a disk to be formatted in ";
  792.     }
  793.     else {
  794.         Speak("Hi ho!  I am ready for the next backup disk.");
  795.         diskPrompt = "Insert the next backup disk in ";
  796.     }
  797.  
  798.     do {
  799.         if (doFormat) {
  800.             Inhibit(destDrive, 1);        /* Inhibit disk validation. */
  801.             if (!RequestDisk(mainWindow, destDrive, diskPrompt)) {
  802.                 Inhibit(destDrive, 0);    /* Uninhibit the drive. */
  803.                 return ERR_ABORT;
  804.             }
  805.  
  806.              /* Don't put the colon in the volume name - 
  807.              * FormatDisk will happily use it as part of 
  808.              * the name rather than as a delimiter. 
  809.              */
  810.  
  811.             DS2Str(datestr, "%02m-%02d-%02y", now);
  812.             sprintf(destVol,"Backup %s.%d", datestr, diskNumber);
  813.  
  814.             if (status = FormatDisk(destDrive, destVol)) {
  815.                 sprintf(conmsg,
  816.                     "I got error %d while formatting.  Sorry about that.\n",
  817.                     status);
  818.                 TypeAndSpeak(conmsg);
  819.                 ++errorCount;
  820.                 SetErrorGadget();
  821.             }
  822.         }
  823.         else {                            /* Don't format disk. */
  824.             if (!RequestDisk(mainWindow, destDrive, diskPrompt)) {
  825.                 return ERR_ABORT;
  826.             }
  827.             if (GetVolumeName(destDrive, destVol) && *destVol)
  828.                 status = 0;
  829.             else
  830.                 status = ERROR_NO_DISK;
  831.         }
  832.     } while (status);
  833.  
  834.     strcat(destVol, ":");            /* add colon */
  835.  
  836.     if ( (totalSize = TotalDiskBlocks(destVol)) < 0)
  837.         status = -totalSize;
  838.     else if ((sizeLeft = DiskBlocksLeft(destVol)) < 0)
  839.         status = -sizeLeft;
  840.     else {
  841.         SetGauge(sizeLeft, totalSize);
  842.         SaveContext();
  843.         if (create_dir && (currentDir != NULL) )
  844.             status = NewDir(currentDir->filename);
  845.     }
  846.     if (!status) {
  847.         Header();
  848.         SetCurVolumeGadget(destVol);
  849.     }
  850.     return status;
  851. }
  852. ^L
  853. /* Remove a file node from the list.
  854.  * Called with:
  855.  *        node:        file node pointer
  856.  *        list:        file list header
  857.  * Returns:
  858.  *        nothing
  859.  */
  860. RemFile(node,list)
  861.     T_FILE *node; T_FILE_LIST *list;
  862. {
  863.     if (node->previous)
  864.         node->previous->next = node->next;
  865.  
  866.     if (node->next)
  867.         node->next->previous = node->previous;
  868.  
  869.     if (node == list->first_file)
  870.         list->first_file = node->next;
  871.  
  872.     if (node == list->last_file)
  873.         list->last_file = node->previous;
  874.  
  875.     if (!node->is_dir) FreeFile(node);
  876. }
  877.  
  878. /* Restore the disk context to what it was when we started this volume. */
  879.  
  880. int
  881. RestoreContext()
  882. {
  883.     T_FILE *newNode, *oldNode;
  884.     int status = ERR_NONE;
  885.  
  886.     
  887.     currentDir = NULL;
  888.     DisposeList(&mainList);                /* free up main list */
  889.  
  890.     for (oldNode = savedList.first_file; oldNode; oldNode = oldNode->next) {
  891.         if (! (newNode = AddFile(oldNode->filename, oldNode->blocks,
  892.                 oldNode->is_dir, &mainList))) {
  893.             status = ERROR_NO_FREE_STORE;
  894.             break;
  895.         }
  896.  
  897.         /* If the old node is the saved current directory, the new node is
  898.          * then the current directory node.
  899.          */
  900.         if (oldNode == savedDir)    currentDir = newNode;
  901.     }
  902.  
  903.     return status;
  904. }
  905.  
  906. /* Save the context (file list, etc.) of the current volume in case we
  907.  * have an error and must restart.  
  908.  */
  909. int
  910. SaveContext()
  911. {
  912.     T_FILE *newNode, *oldNode;
  913.     int status = ERR_NONE;
  914.  
  915.     
  916.     savedDir = NULL;
  917.     DisposeList(&savedList);                /* free up old list */
  918.  
  919.     for (oldNode = mainList.first_file; oldNode; oldNode = oldNode->next) {
  920.         if (! (newNode = AddFile(oldNode->filename, oldNode->blocks,
  921.                 oldNode->is_dir, &savedList))) {
  922.             status = ERROR_NO_FREE_STORE;
  923.             break;
  924.         }
  925.  
  926.         /* If the old node is the current directory, the new node is
  927.          * then its counterpart.
  928.          */
  929.         if (oldNode == currentDir)     savedDir = newNode;
  930.     }
  931.     return status;
  932. }
  933.